/**
 * TranslationAbstract.java
 *
 * Copyright (C) 2009,  Tomislav Milkovic
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package extjsdyntran.translation;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import java.util.zip.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import extjsdyntran.servlets.Constants;
import extjsdyntran.translation.store.StoreManager;

/**
 * @description Abstract base class for translation service.
 * When creating custom translation service, new class must be extended from this one. 
 */
public abstract class TranslationServiceAbstract  implements ITranslationService {
	private static final long serialVersionUID = 1L;
	public static final String defaultLanguage = "ENGLISH";
	final protected Logger logger = LoggerFactory.getLogger(this.getClass());
	
	protected Hashtable<String, Properties> translations = new Hashtable<String, Properties>();
	
	protected String username;
	protected String password;
	//mostly used for custom service where to save additional data
	protected String path;
	
	protected Properties props = new Properties();

	public TranslationServiceAbstract(String username , String password , String path) {
		super();
		this.username = username;
		this.password = password;
		this.path = path;
		logger.info("Service is initialized!");
	}
	
	
	private void compressSingleCache(String language, Properties lang, ZipOutputStream zout) throws Exception{
		
		ByteArrayOutputStream langout=new ByteArrayOutputStream();
		lang.store(langout, "");		
		
        zout.putNextEntry(new ZipEntry(language + ".properties")); 
        zout.write(langout.toByteArray(),0,langout.size());
        zout.closeEntry(); 
	}
	
	protected ByteArrayOutputStream compressCache(String language) throws Exception{
		ByteArrayOutputStream bout=new ByteArrayOutputStream();
		ZipOutputStream zout=new ZipOutputStream(bout);
		Properties lang = null;
		
		if (language ==null){
			Enumeration<String> keys = this.translations.keys();
			Enumeration<Properties> values = this.translations.elements();
			while (keys.hasMoreElements()){			
				String name = keys.nextElement();
				lang =  values.nextElement();
				compressSingleCache(name,lang,zout);
			}
			
			zout.finish();
			return bout;			
		}
		
		if (translations.containsKey(language)){
			lang = translations.get(language);
			compressSingleCache(language,lang,zout);
			zout.finish(); 
			return bout;
		}
	
		return bout ; 
	}
    
	

	 
	protected boolean deCompressCache(byte[]  bin) throws Exception{
		
        ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(bin));
        ZipEntry zEntry = null;
        boolean result = false;
        
        while( (zEntry = zin.getNextEntry()) != null) {
           
           int size;
           byte[] buffer = new byte[1024];           
           ByteArrayOutputStream bos = new ByteArrayOutputStream();           
           
           while ((size = zin.read(buffer, 0, buffer.length)) != -1) {
        	   bos.write(buffer, 0, size);
        	   }
           
           
           String lang =  zEntry.getName();
           int whereDot = lang.lastIndexOf('.');
           String ext = lang.substring(whereDot+1,lang.length());
           lang = lang.substring(0, whereDot);
           
           if (!("properties".equals(ext))) continue;
           
           Properties language = translations.get(lang);           
           if (language == null) language = new Properties();     
           
           ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
           language.load(bis);     
           
           if (!translations.contains(language)) translations.put(lang,language);
           
           bos.close();
           bis.close();
            
           result = true;
        }
        zin.close();
		
		return result;
	}
	
	
	protected static <T extends Enum<T>> String[] enumNameToStringArray(T[] values) {  
	    int i = 0;  
	    String[] result = new String[values.length];  
	    for (T value: values) {  
	        result[i++] = value.name();  
	    }  
	    return result;  
	}  
	
	protected static  String[] enumerationToStringArray( Hashtable<?,?> values) {  
	    
	    String[] result = new String[values.size()];
	    int i =0;
	    Enumeration<?> keys = values.keys();
		while (keys.hasMoreElements()){
			result[i++] = (String)keys.nextElement();  
		}
		
	    return result;  
	}  
	
	
	public boolean appendTranslationToCache(String language, Properties p){
		Properties lang = translations.get(language);
		if(lang==null) {
			translations.put(language, p);
			return true;
		}
		
		lang.putAll(p);		
		return true;
	}
	
	//store factory will call store service to load data, 
	//store service will call  this to load translations to translation service memory cache
	public boolean appendTranslationToCache(String language, String fromValue, String toValue){
		
		Properties lang = translations.get(language);
		if(lang==null) {
			lang = new Properties();
			translations.put(language, lang);
		}
		lang.put(fromValue, toValue);

		return true;
	}
	
	public Hashtable<String, Properties> getAllTranslations(){
		 return this.translations;
	}

	public Properties getTranslations(String language){
		 return this.translations.get(language);
	}
	
	public String getTranslationsToJson(String language){
		if (defaultLanguage.equals(language)){
			return Constants.json_false;
		}
		
		try {

			Properties lang = translations.get(language);
			if (lang == null){
				logger.error("Invalid value ({}) for parameter \"language\" when trying to get language translations.", language);
				return Constants.json_false;
			}


			JsonArray jarr = new JsonArray();
			Enumeration<?> en = lang.elements();
			Set<Entry<Object, Object>> set = lang.entrySet();
			for(Entry e : set){
				String key = (String) e.getKey();
				String value = (String) e.getValue();
				JsonObject jp = new JsonObject();				
				jp.addProperty(defaultLanguage, key);
				jp.addProperty(language, value);
				jarr.add(jp);
			}
			Gson gs = new Gson();
			return gs.toJson(jarr);

		} catch (Exception e) {
			logger.error("Error when converting translations pairs to JSON",e);
			return Constants.json_false;
		}
	}
	
	//http actions call call this to save lang pair to storage
	public boolean saveTranslation( String toLanguage, String fromValue, String toValue){

		if( StoreManager.saveTranslation( toLanguage, fromValue, toValue)){
			Map lang = getTranslations(toLanguage);
			lang.put(fromValue, toValue);
			return true;
		}else return false;
	}
	
	//http actions call call this to delete lang pair from storage
	public boolean deleteTranslation(String fromLanguage, String fromValue){

		if (StoreManager.deleteTranslation(fromLanguage, fromValue)){
			Map lang = getTranslations(fromLanguage);
			lang.remove(fromValue);
			return true;
		}else return false;
	}
	
	
	//http actions call call this to save all lang pair to storage
	public boolean saveLanguages(String language) {
		
		if (language == null) {
			logger.error("Invalid value ({}) for parameter \"language\" when trying to save language.", language);
			return false;
		}
		
		return StoreManager.save(this,language);

	}
	
	//http actions call call this to load all lang pair from storage
	protected void loadLanguages(String [] lg) {		
		StoreManager.load(this,lg);
	}

//	
//	public static String[] mergeStringArrays(String array1[], String array2[]) {
//		if (array1 == null || array1.length == 0) return array2;
//		if (array2 == null || array2.length == 0)return array1;
//		List<String> result = new ArrayList<String>(Arrays.asList(array1));
//		result.retainAll(Arrays.asList(array2));
//		
//		return result.toArray(new String[result.size()]);
//	}
	
	
}
